/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.misc.dts.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.vacoor.nothing.common.util.IOUtils;
import org.vacoor.nothing.misc.dts.conv.DocumentToPdfTask;
import org.vacoor.nothing.misc.dts.conv.PdfToSwfTask;
import org.vacoor.nothing.misc.dts.domain.Document;
import org.vacoor.nothing.misc.dts.oo.comp.UnoBridge;
import org.vacoor.nothing.misc.dts.oo.util.UnoUtils;
import org.vacoor.nothing.misc.dts.service.DocumentService;
import org.vacoor.nothing.misc.dts.util.PDFUtils;
import org.vacoor.nothing.web.service.ServiceException;
import org.vacoor.nothing.web.service.StorageService;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * TODO Bridge 经常断, 需要处理, 另外限制下 Bridge 任务数
 *
 * @author vacoor
 */
@Service
public class DocumentServiceImpl implements DocumentService {
    private static final Logger LOG = LoggerFactory.getLogger(DocumentServiceImpl.class);

    @Autowired
    private StorageService storageService;
    @Autowired
    protected UnoBridge unoBridge;
    private LinkedBlockingQueue<Runnable> workQueue;
    private ThreadPoolExecutor threadPoolExecutor;
    private Map<String/*did*/, Document> docs = new HashMap<String, Document>();

    public DocumentServiceImpl() {
        this.workQueue = new LinkedBlockingQueue<Runnable>();
        this.threadPoolExecutor = new ThreadPoolExecutor(10, 15, 10, TimeUnit.MINUTES, workQueue);
    }

    @Override
    public List<Document> getDocuments() {
        return Collections.unmodifiableList(new ArrayList<Document>(docs.values()));
    }

    @Override
    public Document getDocument(String did) {
        return docs.get(did);
    }

    @Override
    public String/*did*/ addDocument(MultipartFile multipart) {
        try {
            // 验证文件头
            final String hash = storageService.store(multipart.getInputStream());
            final String filename = multipart.getOriginalFilename();

            Document doc = new Document();
            doc.setId(UUID.randomUUID().toString());    // TODO
            doc.setName(filename);
            doc.setType(UnoUtils.getFileExtension(filename));    // extension??
            doc.setSize(multipart.getSize());
            doc.setHash(hash);

            // saveDoc
            docs.put(doc.getId(), doc);

            byte[] header = new byte[PDFUtils.PDF_HEADER.length];
            InputStream upload = storageService.readAsStream(hash);
            IOUtils.readFully(upload, header, true);

            /**
             * if file type is not pdf, execute convert
             */
            if (!PDFUtils.isPdfHeader(header)) {
                doc.setStatus(Document.Status.WAIT);
                threadPoolExecutor.execute(new Worker(doc.getId()));
            } else {
                doc.setStatus(Document.Status.SUCCESS);
                doc.setPdfHash(hash);
            }

            return doc.getId();
        } catch (Throwable t) {
            throw new ServiceException(t);
        }
    }

    private String snapshotBaseDir = System.getProperty("user.home") + "/Desktop/snapshot";

    @Override
    public InputStream getSnapshot(String pdfHash, int page) {
        try {
            // 1. 获取缓存
            File swf = new File(snapshotBaseDir, pdfHash + "-" + page + ".swf");
            if (!swf.exists()) {
                File pdf = storageService.readAsFile(pdfHash);
                if (pdf == null) {
                    return null;
                }
                new PdfToSwfTask(pdf, swf).execute(page);
                // 放入缓存
            }
            return new FileInputStream(swf);
        } catch (Throwable t) {
            throw new ServiceException(t);
        }
    }

    protected void runTask() {
    }

    private class Worker implements Runnable {
        private final String did;

        private Worker(String did) {
            this.did = did;
        }

        @Override
        public void run() {
            Document doc = docs.get(did);
            File source;
            if (doc == null || (source = storageService.readAsFile(doc.getHash())) == null) {
                return;
            }

            doc.setStatus(Document.Status.RUNNING);   // update status to running
            try {
                File tempFile = File.createTempFile("task-", ".tmp");
                FileOutputStream fOut = new FileOutputStream(tempFile);
                if (!unoBridge.isConnected()) {
                    unoBridge.connect();
                }

                new DocumentToPdfTask(source, fOut).execute(unoBridge);

                FileInputStream fIn = new FileInputStream(tempFile);
                int totalPages = PDFUtils.getTotalPages(fIn);

                if (totalPages < 1) {
                    return;
                }

                // task.execute(unoBridge);
                String pdfHash = storageService.store(new FileInputStream(tempFile));

                doc.setStatus(Document.Status.SUCCESS); // update status successful
                doc.setTotalPages(totalPages);
                doc.setPdfHash(pdfHash);
            } catch (Exception e) {
                // 这种任务应该失败就失败了, 大不了让用户重新点击启动
                LOG.warn("task failed... {}", e);
                doc.setStatus(Document.Status.FAILED); // update status failed
            } finally {
            }
        }
    }
}
